import sys
from tkinter import Tk, Label, Entry, Button, Event, END, W
from random import choice
from os import name, path

answer = ''
current_line = 0
words = []


def resource_path(relative_path):
    if getattr(sys, 'frozen', False):  # 判断sys中是否存在frozen变量,即是否是打包程序
        # noinspection PyProtectedMember
        base_path = sys._MEIPASS       # sys._MEIPASS在一些编辑器中会报错，不用理会
    else:
        base_path = path.abspath(".")
    return path.join(base_path, relative_path)


def init(easy: bool = False):
    """ Initialize variables """
    global answer, words
    words = []
    words_pre = open(resource_path('dict/dict.clean.txt'), 'r').readlines()
    if easy:
        words_pre = open(resource_path('dict/dict.easy.txt'), 'r').readlines()
    for word in words_pre:
        words.append(word[:-1])
    answer = choice(words)  # Select a word
    entries[(0, 0)].config(state="normal")  # Set cursor to the first blank
    entries[(0, 0)].focus_set()


def replay():
    """ Reset the game without reload the application """
    init(easy=True)
    global answer, current_line, LED
    answer = choice(words)
    current_line = 0  # Reset index indicator
    for x in range(5):
        for y in range(5):
            # Clear a single entry
            entries[(x, y)].config(state="normal")
            entries[(x, y)].delete(0, END)
            entries[(x, y)].config(state="readonly", fg='black')
        # Reset button state
        buttons[x][0].config(state="disabled")
        buttons[x][1].config(state="disabled")
    entries[(0, 0)].config(state="normal")  # Set cursor to the first blank
    entries[(0, 0)].focus_set()
    LED.config(text="提示：")  # Reset hint state


def clean_line():
    """ Clean a single line """
    global current_line
    x = current_line  # Get index indicator value
    for y in range(5):
        # Clear a single entry
        entries[(x, y)].config(state="normal")
        entries[(x, y)].delete(0, END)
        entries[(x, y)].config(state="readonly")
        entries[(x, y)].config(fg="black")
    entries[(x, 0)].config(state="normal")  # Set cursor to the first blank of current line
    entries[(x, 0)].focus_set()


def verify():
    """ Check and color each letter """
    global current_line, LED
    x = current_line  # Get index indicator value
    guess = ''
    for y in range(5):
        entries[(x, y)].config(state='readonly')  # To show color, we have to use readonly rather than disabled.
        guess += entries[(x, y)].get()  # Get a single letter
        if guess[-1] == answer[y]:
            entries[(x, y)].config(fg="green")  # If the position and letter are both correct, green it.
        elif guess[-1] in answer:
            entries[(x, y)].config(fg="orange")  # If the letter is included but the position is wrong, orange it.
        else:
            entries[(x, y)].config(fg="red")  # If nothing is correct, red it.
    words_full = open(resource_path('dict/dict.clean.txt'), 'r').readlines()
    for index in range(len(words_full)):
        words_full[index] = words_full[index][:-1]
    if guess not in words_full:
        LED.config(text="所猜单词不在词库中！")
        clean_line()
        current_line -= 1
    elif guess != answer and x != 5 - 1:  # Check if the trails have been completely used.
        entries[(x + 1, 0)].config(state="normal")  # Set cursor to next line.
        entries[(x + 1, 0)].focus_set()
        LED.config(text="红色=位错+字错；橙色=位错+字对；绿色=位对+字对")
    elif guess == answer:
        LED.config(text="恭喜你回答正确！")
    elif x == 5 - 1:
        LED.config(text=f"答案：{answer} 机会用完了，重置再试吧～")
    buttons[x][0].config(state="disabled")
    buttons[x][1].config(state="disabled")


def on_change(event: Event):
    """ Do some checks while inputting """
    global current_line, LED, answer
    entry: Entry = event.widget  # Get where the event was called

    # Find the related index
    idx = None
    for _idx in entries.keys():
        if entries[_idx] == entry:
            idx = _idx
            break
    if idx is None:
        return

    if len(entry.get()) == 0:
        pass  # wait on_delete() for help
    else:
        if len(entry.get()) > 1:  # Cut text because we only accept one letter each.
            orig = entry.get()
            entry.delete(0, END)
            entry.insert(0, orig[0])
        if not ('a' <= entry.get() <= 'z'):  # Only character a~z is allowed.
            entry.delete(0, END)
            LED.config(text="提示：输入必须在a~z之间")
            return
        guess = ''
        x = list(idx)[0]
        for y in range(5):
            if y == list(idx)[1]:
                continue
            entries[(x, y)].config(state='readonly')  # To show color, we have to use readonly rather than disabled.
            guess += entries[(x, y)].get()  # Get a single letter
        if entry.get() in guess:  # You cannot repeat.
            entry.delete(0, END)
            LED.config(text="提示：字母不得重复")
            return
        idx_next = (list(idx)[0], list(idx)[1] + 1)  # Move to next entry
        if (list(idx)[1] + 1) % 5 != 0:  # Is it the end of current line?
            entry.config(state="readonly")
            entries[idx_next].config(state="normal")
            entries[idx_next].focus_set()
        else:  # Show the verify and reset buttons if the line ends.
            entry.config(state="readonly")
            buttons[list(idx)[0]][0].config(state="normal")
            buttons[list(idx)[0]][1].config(state="normal")
            current_line = list(idx)[0]


def on_delete(event: Event):
    """ Move cursor backward """
    entry: Entry = event.widget  # Get where the event was called

    if len(entry.get()) != 0:  # Skip the filled entry
        return

    # Find the related index
    idx = None
    for _idx in entries.keys():
        if entries[_idx] == entry:
            idx = _idx
            break
    if idx is None:
        return

    if list(idx)[1] - 1 >= 0:  # Move cursor backwards if it is not in the first blank
        idx_previous = (list(idx)[0], list(idx)[1] - 1)
        entry.config(state="readonly")
        entries[idx_previous].config(state="normal")
        entries[idx_previous].delete(0, END)
        entries[idx_previous].focus_set()


# Construct GUI
root_window = Tk()  # Create a window
root_window.title("Wordle Game Python")  # Set window name
if name != 'posix':
    root_window.geometry("300x210")
entries = {}  # Save guesses
buttons = {}
for i in range(5):  # Generate entries
    Label(root_window, text=f"第{i + 1}次猜测：").grid(row=i, column=0)  # Label in front of entry
    for j in range(5):
        width = 2
        if name != 'posix':
            width = 4
        entries[(i, j)] = Entry(root_window, width=width)
        entries[(i, j)].grid(row=i, column=j + 1)
        entries[(i, j)].bind("<KeyRelease>", on_change)  # Bind on_change event
        entries[(i, j)].bind("<BackSpace>", on_delete)  # Bind on_delete event
        entries[(i, j)].bind("<Delete>", on_delete)  # Bind on_delete event
        entries[(i, j)].config(state="readonly")  # Disable entry by default
    buttons[i] = [Button(root_window, text="重输", state="disabled", command=clean_line),
                  Button(root_window, text="验证", state="disabled", command=verify)]
    buttons[i][0].grid(row=i, column=6)
    buttons[i][1].grid(row=i, column=7)
LED = Label(root_window, text="提示：")  # To show some hints
LED.grid(row=5, column=0, columnspan=8, sticky=W)
Button(root_window, text="重置并切换到简单词库", command=replay).grid(row=6, column=0, columnspan=8)  # Play again button

if __name__ == '__main__':
    init()  # Clear and set variables
    root_window.mainloop()  # Start message loop
